/*
 * Synchronizing blocks instead of entire methods.
 * Also demonstrates protection of a non-thread-safe class with a thread-safe one.
 */
package cn.nju.zyy.share.criticalsection;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class PairManipulator implements Runnable {
	private PairManager pm;

	public PairManipulator(PairManager pm) {
		this.pm = pm;
	}

	@Override
	public void run() {
		while (true) {
			pm.increment();
		}
	}

	@Override
	public String toString() {
		return "Pair: " + pm.getPair() + " checkCounter = "
				+ pm.checkCounter.get();
	}
	
}

class PairChecker implements Runnable {
	private PairManager pm;

	public PairChecker(PairManager pm) {
		this.pm = pm;
	}

	@Override
	public void run() {
		while (true) {
			pm.checkCounter.incrementAndGet();
			pm.getPair().checkState();
		}
	}

}

public class CriticalSection {
	// Test the two different approaches:
	static void testApproaches(PairManager pm1, PairManager pm2) {
		ExecutorService exec = Executors.newCachedThreadPool();
		PairManipulator pmani1 = new PairManipulator(pm1),
				pmani2 = new PairManipulator(pm2);
		PairChecker pcheck1 = new PairChecker(pm1),
				pcheck2 = new PairChecker(pm2);
		exec.execute(pmani1);
		exec.execute(pmani2);
		exec.execute(pcheck1);
		exec.execute(pcheck2);
		try {
			TimeUnit.MILLISECONDS.sleep(500);
		} catch (InterruptedException e) {
			System.out.println("Sleep interrupted");
		}
		System.out.println("pmani1: " + pmani1 + "\npmani2: " + pmani2);
		System.exit(0);
	}
	
	public static void main(String[] args) {
		PairManager pm1 = new PairManager1(),
				pm2 = new PairManager2();
		testApproaches(pm1, pm2);
	}

}
